Weighted Score: shared multi-stat sort across PoB - #9845
Draft
mcagnion wants to merge 25 commits into
Draft
Conversation
mcagnion
marked this pull request as draft
May 10, 2026 07:07
mcagnion
marked this pull request as ready for review
May 12, 2026 21:40
mcagnion
force-pushed
the
feature/weighted-score
branch
from
July 26, 2026 19:31
359f900 to
0c331b0
Compare
mcagnion
marked this pull request as draft
July 26, 2026 19:44
mcagnion
force-pushed
the
feature/weighted-score
branch
from
July 28, 2026 09:19
7a269ae to
ddeca82
Compare
mcagnion
force-pushed
the
feature/weighted-score
branch
3 times, most recently
from
August 6, 2026 10:43
c48337f to
4042a77
Compare
…nto tree/items - Add Modules/WeightedScore with defaultWeights(), getWeights(), computeRatioScore() - Register WeightedScore entry in data.powerStatList (isWeightedScore flag) - TradeQueryGenerator.WeightedRatioOutputs delegates to WeightedScore.computeRatioScore - TradeQuery.SetStatWeights: add onSave callback, filter isWeightedScore from stat list - CalcsTab.CalculatePowerStat: isWeightedScore branch for heatmap scoring - TreeTab: add Edit Weights... button (shown only when WeightedScore heatmap active) - ItemDBControl: add WeightedScore sort mode and Edit Weights... button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
19 tests across 3 describe blocks: - WeightedScore module: defaultWeights, getWeights, computeRatioScore (neutrality, ranking, edge cases: inf/zero/missing, FullDPS fallback) - TradeQueryGenerator delegation: result matches direct call, ranking preserved - Tree integration: stat registered, power builder completes, powerMax >= 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NotableDBControl was missing the isWeightedScore propagation in BuildSortOrder and the computeRatioScore branch in ListBuilder, causing all notables to score 0 when sorted by Weighted Score. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consumers of powerStatList that need to compute a stat value from a calc output (e.g. RadiusJewelFinder's getImpactValue) cannot read output["WeightedScore"] directly since it is not a real calc field. Provide a getValue(output, build) callback on the stat entry so any consumer can get a meaningful weighted ratio score without needing to know about the WeightedScore module. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LoadModule does not cache — calling it inside getValue on every invocation created a new module table each time, causing memory pressure in tight loops like RadiusJewelFinder compute. Move the LoadModule call to module scope so it executes once at startup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When 'Weighted Score' was selected as Fallback Weight Mode, the timeless jewel finder's generateFallbackWeights read output["WeightedScore"] — a field that does not exist in calc output. This returned 0 for every node, causing all fallback weights to be computed as -100 (or -50 for nodes with a non-unit divisor), making the weighted score sort meaningless. Fix: introduce a getStatValue helper inside generateFallbackWeights that delegates to selection.getValue(rawOutput, build) when present, mirroring the same pattern used in RadiusJewelFinder:getImpactValue. The raw (non-Minion-scoped) output is passed so getValue receives the full calc output as expected by WeightedScore.computeRatioScore. Also guard baseValue == 0 to avoid division by zero for builds with no relevant output stat. Add two tests to TestWeightedScore_spec.lua covering getValue correctness on the powerStatList entry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The fallbackWeightsList dropdown entries were built by copying only stat, transform, and label from data.powerStatList — getValue was silently dropped. As a result, generateFallbackWeights received selection.getValue = nil and could never delegate to the WeightedScore callback, falling back to reading output["WeightedScore"] (a non-existent field) and producing weight = -100 for every node. Fix: copy getValue when building each fallbackWeightsList entry so the callback reaches generateFallbackWeights correctly. Add a test asserting that the constructed entry carries getValue. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Route WeightedScore power reports through FullDPS when active weights require it, reuse the cached base output for WeightedScore getValue, and keep allocated nodes out of the default unallocated report list.
…action entry Replace per-surface Edit Weights buttons (TreeTab heatmap, ItemDB unique sort) with a shared action entry appended to any sort/heatmap dropdown that exposes Weighted Score. NotableDB anoint sort gains the same affordance (no Edit Weights button before). Why: the ItemDB pane is 360 px wide; the previous Edit Weights button shared the same anchor as the League dropdown and forced a mutually exclusive show/hide between League filter and Edit Weights. A user who wanted to filter by League and sort by Weighted Score could not do both. WeightedScore.appendEditWeightsAction(list, openEditor) sentinel-checks for any entry with isWeightedScore and appends a single action entry. Each consumer dropdown selFunc gates on value.isAction: invokes value.action() then restores the prior selection via SelByValue. Tests: 2 new specs in TestWeightedScore_spec (no-op without WS, append plus invocable callback when WS present). Peer-reviewed by Codex (approved, no blocking findings).
Restore the shared accessor, transforms, and FullDPS fallback expected by Trade Query after the origin/dev rebase.
Avoid generating a Minion Weighted Score entry and hide the score from modifier sort menus that only read output fields.
Keep negative scores and request FullDPS only when the active weights need it.
Calculate Weighted Score candidates with the matching Full DPS context and retain the baseline output for Tree fallback scoring.
Evaluate contextual metrics against the displayed item baseline so Weighted Score and Full DPS rank candidates correctly.
Forward the callback when Reset reopens the editor so the subsequent Save invalidates caller caches.
Evaluate crafted prefix and suffix candidates through the contextual stat API so Weighted Score uses its baseline and Full DPS requirements instead of tying every modifier at zero.
Keep crafted affix, Compare Power, and Timeless fallback scoring aligned with saved weights.
mcagnion
force-pushed
the
feature/weighted-score
branch
from
August 15, 2026 15:55
074c6f6 to
c7fe989
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of the problem being solved:
The Item DB list, Anoint Notable picker, Tree heatmap, and Power Report all rank candidates against a single chosen stat: Life, EHP, Total DPS, Armour, etc. When a build cares about a combination of stats (e.g., 100% Full DPS, 50% EHP, 50% Life Regen, 30% Movement Speed) sorting by a single stat can only approximate that combination.
This PR adds a
Weighted Scoreoption across the shared stat-based ranking surfaces, including the Item DB, Anoint Notable picker, Tree and Compare Power Reports, item modifier and crafted-affix selectors, and Timeless Jewel fallback weighting. The weights reuse the Trade Query'sStat Weight Multipliers, so configured trade weights produce the same ranking everywhere.An
Edit Weights...entry opens the existing Stat Weight Multipliers popup. Saving immediately refreshes surfaces that rank existing candidates; the Timeless Jewel finder preserves its explicitGenerateaction after weights are changed.Steps taken to verify a working solution:
Items tab: Unique DB list sorted by Weighted Score
Inline "Edit Weights..." entry in the Sort dropdown
Anoint picker: Notable list sorted by Weighted Score
Power Report sorted by Weighted Score: